﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using C4F.DevKit.Calendar.Parser;
using System.IO;

namespace C4F.DevKit.Calendar
{
    /// <summary>
    /// A class which represents a calendar appointment.
    /// </summary>
    public class Appointment
    {
        #region Private Members

        private Guid _UID;

        private List<Appointment> _occurrences;

        private int _sequenceNumber;

        private bool _isPartOfSeries;

        private List<string> _extraProperties;

        private string _summary;

        private string _location;

        private string _url;

        private DateTime _startDateTime;

        private DateTime _endDateTime;

        private bool _isAllDayEvent;

        private DateTime _occurrence;

        private DateTime _timestamp;

        private List<Attendee> _attendees;

        private Reminder _reminder;

        private RecurrenceRule _recurrenceRule;

        private string _notes;

        #endregion

        #region Internal Methods

        internal void SetUID(Guid uid)
        {
            _UID = uid;
        }

        #endregion

        #region Public Constructors

        /// <summary>
        /// Creates a new appointment.
        /// </summary>
        public Appointment()
        {
            Attendees = new List<Attendee>();
            SetUID(Guid.NewGuid());
            Timestamp = DateTime.Now;
            Occurrences = new List<Appointment>();
            ExtraProperties = new List<string>();
        }

        #endregion

        #region Internal Properties

        internal List<Appointment> Occurrences
        {
            get
            {
                return _occurrences;
            }
            set
            {
                _occurrences = value;
            }
        }

        internal int SequenceNumber
        {
            get
            {
                return _sequenceNumber;
            }
            set
            {
                _sequenceNumber = value;
            }
        }

        internal bool IsPartOfSeries
        {
            get
            {
                return _isPartOfSeries;
            }
            set
            {
                _isPartOfSeries = value;
            }
        }

        internal List<string> ExtraProperties
        {
            get
            {
                return _extraProperties;
            }
            set
            {
                _extraProperties = value;
            }
        }
        
        #endregion

        #region Public Properties

        /// <summary>
        /// A summary of the appointment.
        /// </summary>
        public string Summary
        {
            get
            {
                return _summary;
            }
            set
            {
                _summary = value;
            }
        }

        /// <summary>
        /// The location of the appointment.
        /// </summary>
        public string Location
        {
            get
            {
                return _location;
            }
            set
            {
                _location = value;
            }
        }

        /// <summary>
        /// The unique identifier for the appointment.
        /// </summary>
        public Guid UID
        {
            get
            {
                return _UID;
            }
        }

        /// <summary>
        /// The URL of the appointment.
        /// </summary>
        public string URL
        {
            get
            {
                return _url;
            }
            set
            {
                _url = value;
            }
        }

        /// <summary>
        /// The starting date and time of the appointment.
        /// </summary>
        public DateTime StartDateTime
        {
            get
            {
                return _startDateTime;
            }
            set
            {
                _startDateTime = value;
            }
        }

        /// <summary>
        /// The ending date and time of the appointment.
        /// </summary>
        public DateTime EndDateTime
        {
            get
            {
                return _endDateTime;
            }
            set
            {
                _endDateTime = value;
            }
        }

        /// <summary>
        /// The start date and time of the appointment, in the time
        /// zone of the current system.
        /// </summary>
        public DateTime LocalStartDateTime
        {
            get
            {
                return TimeZone.CurrentTimeZone.ToLocalTime(_startDateTime);
            }
        }

        /// <summary>
        /// The end date and time of the appointment, in the time
        /// zone of the current system.
        /// </summary>
        public DateTime LocalEndDateTime
        {
            get
            {
                return TimeZone.CurrentTimeZone.ToLocalTime(_endDateTime);
            }
        }

        /// <summary>
        /// Whether or not this appointment is an all-day event.
        /// </summary>
        public bool IsAllDayEvent
        {
            get
            {
                return _isAllDayEvent;
            }
            set
            {
                _isAllDayEvent = value;
            }
        }

        /// <summary>
        /// The specific occurence of this appointment, out of a series of appointments.
        /// </summary>
        public DateTime Occurrence
        {
            get
            {
                return _occurrence;
            }
            set
            {
                _occurrence = value;
            }
        }

        /// <summary>
        /// The creation date and time of the appointment.
        /// </summary>
        public DateTime Timestamp
        {
            get
            {
                return _timestamp;
            }
            set
            {
                _timestamp = value;
            }
        }

        /// <summary>
        /// The list of attendees for the appointment.
        /// </summary>
        public List<Attendee> Attendees
        {
            get
            {
                return _attendees;
            }
            set
            {
                _attendees = value;
            }
        }

        /// <summary>
        /// The reminder for the event.
        /// </summary>
        public Reminder Reminder
        {
            get
            {
                return _reminder;
            }
            set
            {
                _reminder = value;
            }
        }

        /// <summary>
        /// The rule which specifies how the appointment repeats, if it does at all.
        /// </summary>
        public RecurrenceRule RecurrenceRule
        {
            get
            {
                return _recurrenceRule;
            }
            set
            {
                _recurrenceRule = value;
            }
        }

        /// <summary>
        /// General notes about the appointment.
        /// </summary>
        public string Notes
        {
            get
            {
                return _notes;
            }
            set
            {
                _notes = value;
            }
        }

        /// <summary>
        /// Gets and sets whether or not this event recurs on Sundays.
        /// </summary>
        public bool RecurOnSunday
        {
            get
            {
                return DoesRecurOnDay(Weekday.Sunday);
            }
            set
            {
                RecurOnDay(Weekday.Sunday);
            }
        }

        /// <summary>
        /// Gets and sets whether or not this event recurs on Mondays.
        /// </summary>
        public bool RecurOnMonday
        {
            get
            {
                return DoesRecurOnDay(Weekday.Monday);
            }
            set
            {
                RecurOnDay(Weekday.Monday);
            }
        }

        /// <summary>
        /// Gets and sets whether or not this event recurs on Tuesdays.
        /// </summary>
        public bool RecurOnTuesday
        {
            get
            {
                return DoesRecurOnDay(Weekday.Tuesday);
            }
            set
            {
                RecurOnDay(Weekday.Tuesday);
            }
        }

        /// <summary>
        /// Gets and sets whether or not this event recurs on Wednesdays.
        /// </summary>
        public bool RecurOnWednesday
        {
            get
            {
                return DoesRecurOnDay(Weekday.Wednesday);
            }
            set
            {
                RecurOnDay(Weekday.Wednesday);
            }
        }

        /// <summary>
        /// Gets and sets whether or not this event recurs on Thursdays.
        /// </summary>
        public bool RecurOnThursday
        {
            get
            {
                return DoesRecurOnDay(Weekday.Thursday);
            }
            set
            {
                RecurOnDay(Weekday.Thursday);
            }
        }

        /// <summary>
        /// Gets and sets whether or not this event recurs on Fridays.
        /// </summary>
        public bool RecurOnFriday
        {
            get
            {
                return DoesRecurOnDay(Weekday.Friday);
            }
            set
            {
                RecurOnDay(Weekday.Friday);
            }
        }

        /// <summary>
        /// Gets and sets whether or not this event recurs on Saturdays.
        /// </summary>
        public bool RecurOnSaturday
        {
            get
            {
                return DoesRecurOnDay(Weekday.Saturday);
            }
            set
            {
                RecurOnDay(Weekday.Saturday);
            }
        }                

        #endregion

        #region Public Methods

        /// <summary>
        /// Validates that the appointment has all required fields (Summary, StartDateTime, EndDateTime).
        /// Also validates that the end of the appointment is either at the same time or later than
        /// the start.
        /// </summary>
        /// <returns>True if the appointment is valid, false otherwise.</returns>
        public bool Validate()
        {
            return (StartDateTime != new DateTime()) &&
                (EndDateTime != new DateTime()) &&
                StartDateTime <= EndDateTime &&
                (!string.IsNullOrEmpty(Summary));
        }

        /// <summary>
        /// Saves the appointment to a calendar file, adding it to the end.
        /// </summary>
        /// <param name="filePath">The file path of the calendar to add the appointment to.</param>
        public void Save(string filePath)
        {
            using (FileStream file = new FileStream(filePath, FileMode.Open))
            {
                Save(file);
            }
        }

        /// <summary>
        /// Saves the appointment to a calendar file, adding it to the end.
        /// </summary>
        /// <param name="file">The file stream of the calendar to save the appointment to.</param>
        public void Save(FileStream file)
        {
            Writer writer = new Writer(file);
            writer.AddToCalendar(this);
            writer.Close();
        }

        /// <summary>
        /// Adds a certain weekday on which to recur. This method should
        /// only be used when the recurrence rule is set to use a weekly
        /// recurrence.
        /// </summary>
        /// <param name="weekday">The weekday on which to recur.</param>
        public void RecurOnDay(Weekday weekday)
        {

            if (DoesRecurOnDay(weekday))
            {
                return;
            }

            this.RecurrenceRule.MonthWeekdays.Add(new MonthWeekday(weekday, 0));
        }

        /// <summary>
        /// Tells whether this appointment recurs on a certain weekday.
        /// This method should only be used on appointments with weekly
        /// recurrence rules.
        /// </summary>
        /// <param name="weekday">The weekday to check for recurrence.</param>
        /// <returns>Whether or not the appointment recurs on the given day.</returns>
        public bool DoesRecurOnDay(Weekday weekday)
        {
            foreach (MonthWeekday mw in this.RecurrenceRule.MonthWeekdays)
            {
                if (mw.Weekday == weekday && mw.WeekNumber == 0)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Sets this appointment to recur every day.
        /// </summary>
        public void SetDailyRecurrence()
        {
            RecurrenceRule = new RecurrenceRule(Frequency.Daily);
        }

        /// <summary>
        /// Sets this apppointment to recur every day.
        /// </summary>
        /// <param name="count">The number of times for the appointment to recur.</param>
        public void SetDailyRecurrence(int count)
        {
            SetRecurrence(Frequency.Daily, count);
        }

        /// <summary>
        /// Sets this appointment to recur every day.
        /// </summary>
        /// <param name="end">The date on which to end the recurrence.</param>
        public void SetDailyRecurrence(DateTime end)
        {
            SetRecurrence(Frequency.Daily, end);
        }

        /// <summary>
        /// Sets this appointment to recur once every week, on the day of
        /// the week on which it starts.
        /// </summary>
        public void SetWeeklyRecurrence()
        {
            List<Weekday> weekdays = new List<Weekday>();
            weekdays.Add((Weekday) ((int) StartDateTime.DayOfWeek));
            SetWeeklyRecurrence(weekdays);
        }

        /// <summary>
        /// Sets this appointment to recur once every week.
        /// </summary>
        /// <param name="count">The number of times for the appointment to recur.</param>
        public void SetWeeklyRecurrence(int count)
        {
            SetRecurrence(Frequency.Weekly, count);
        }

        /// <summary>
        /// Sets this appointment to recur once every week.
        /// </summary>
        /// <param name="end">The date on which to end the recurrence.</param>
        public void SetWeeklyRecurrence(DateTime end)
        {
            SetRecurrence(Frequency.Weekly, end);
        }

        /// <summary>
        /// Sets this appointment to recur weekly.
        /// </summary>
        /// <param name="weekdays">The days of the week on which to recur.</param>
        public void SetWeeklyRecurrence(List<Weekday> weekdays)
        {
            RecurrenceRule = new RecurrenceRule(Frequency.Weekly);

            foreach (Weekday weekday in weekdays)
            {
                RecurrenceRule.MonthWeekdays.Add(new MonthWeekday(weekday, 0));
            }
        }

        /// <summary>
        /// Sets this appointment to recur weekly.
        /// </summary>
        /// <param name="weekdays">The days of the week on which to recur.</param>
        /// <param name="count">The number of times for the appointment to recur.</param>
        public void SetWeeklyRecurrence(List<Weekday> weekdays, int count)
        {
            SetRecurrence(Frequency.Weekly, count);
        }

        /// <summary>
        /// Sets this appointment to recur weekly.
        /// </summary>
        /// <param name="weekdays">The days of the week on which to recur.</param>
        /// <param name="end">The date on which to end the recurrence.</param>
        public void SetWeeklyRecurrence(List<Weekday> weekdays, DateTime end)
        {
            SetRecurrence(Frequency.Weekly, end);
        }

        /// <summary>
        /// Sets this appointment to recur once a month, on the day of the month
        /// on which this appointment starts.
        /// </summary>
        public void SetMonthlyRecurrence()
        {
            RecurrenceRule = new RecurrenceRule(Frequency.Monthly);
        }

        /// <summary>
        /// Sets this appointment to recur once a month, on the day of the month
        /// on which this appointment starts.
        /// </summary>
        /// <param name="count">The number of times for the appointment to recur.</param>
        public void SetMonthlyRecurrence(int count)
        {
            SetRecurrence(Frequency.Monthly, count);
        }

        /// <summary>
        /// Sets this appointment to recur once a month, on the day of the month
        /// on which this appointment starts.
        /// </summary>
        /// <param name="end">The date on which to end the recurrence.</param>
        public void SetMonthlyRecurrence(DateTime end)
        {
            SetRecurrence(Frequency.Monthly, end);
        }

        /// <summary>
        /// Sets this appointment to recur once a year, on the date on which the
        /// appointment starts.
        /// </summary>
        public void SetYearlyRecurrence()
        {
            RecurrenceRule = new RecurrenceRule(Frequency.Yearly);
        }

        /// <summary>
        /// Sets this appointment to recur once a year, on the date on which the
        /// appointment starts.
        /// </summary>
        /// <param name="count">The number of times for the appointment to recur.</param>
        public void SetYearlyRecurrence(int count)
        {
            SetRecurrence(Frequency.Yearly, count);
        }

        /// <summary>
        /// Sets this appointment to recur once a year, on the date on which the
        /// appointment starts.
        /// </summary>
        /// <param name="end">The date on which to end the recurrence.</param>
        public void SetYearlyRecurrence(DateTime end)
        {
            SetRecurrence(Frequency.Monthly, end);
        }

        /// <summary>
        /// Sets a recurrence rule for this appointment.
        /// </summary>
        /// <param name="freq">The frequency type of this recurrence.</param>
        /// <param name="count">The number of times for the appointment to recur.</param>
        public void SetRecurrence(Frequency freq, int count)
        {
            RecurrenceRule = new RecurrenceRule(freq);
            RecurrenceRule.Count = count;
        }

        /// <summary>
        /// Sets a recurrence rule for this appointment.
        /// </summary>
        /// <param name="freq">The frequency type of this recurrence.</param>
        /// <param name="end">The date on which to end the recurrence.</param>
        public void SetRecurrence(Frequency freq, DateTime end)
        {
            RecurrenceRule = new RecurrenceRule(freq);
            RecurrenceRule.EndDateTime = end;
        }

        #endregion
    }
}
